feat: React UI v2.0 — Phase 0 backend foundation + Phase 1 scaffold start#17
Conversation
| include_in_schema=False, | ||
| ) | ||
| async def _legacy_incidents_detail(path: str) -> RedirectResponse: | ||
| return RedirectResponse(url=f"/api/v1/sessions/{path}", status_code=308) |
| ) | ||
| async def _legacy_investigate_subpath(path: str) -> RedirectResponse: | ||
| return RedirectResponse( | ||
| url=f"/api/v1/investigate/{path}", status_code=308, |
| include_in_schema=False, | ||
| ) | ||
| async def _legacy_incidents_detail(path: str) -> RedirectResponse: | ||
| return RedirectResponse(url=f"/api/v1/sessions/{path}", status_code=308) |
| ) | ||
| async def _legacy_investigate_subpath(path: str) -> RedirectResponse: | ||
| return RedirectResponse( | ||
| url=f"/api/v1/investigate/{path}", status_code=308, |
| include_in_schema=False, | ||
| ) | ||
| async def _legacy_incidents_detail(path: str) -> RedirectResponse: | ||
| return RedirectResponse(url=f"/api/v1/sessions/{path}", status_code=308) |
| ) | ||
| async def _legacy_investigate_subpath(path: str) -> RedirectResponse: | ||
| return RedirectResponse( | ||
| url=f"/api/v1/investigate/{path}", status_code=308, |
| include_in_schema=False, | ||
| ) | ||
| async def _legacy_incidents_detail(path: str) -> RedirectResponse: | ||
| return RedirectResponse(url=f"/api/v1/sessions/{path}", status_code=308) |
| ) | ||
| async def _legacy_investigate_subpath(path: str) -> RedirectResponse: | ||
| return RedirectResponse( | ||
| url=f"/api/v1/investigate/{path}", status_code=308, |
Code-review follow-ups for Phase 0 Task 3 (PR for /api/v1/sessions/
{id}/full bootstrap endpoint):
- Move the ``from runtime import api_session_full`` import from inside
``build_app`` to module-level alongside the other ``from runtime.…``
imports — restores consistency with the rest of api.py.
- Add a docstring note in api_session_full.py distinguishing it from
api_dedup.py: this module requires ``app.state.orchestrator`` so it
cannot be used with bare ``FastAPI()`` test fixtures — call
``build_app(cfg)`` instead.
- Tighten ``list[dict]`` / ``dict[str, dict]`` annotations to
``list[dict[str, Any]]`` / ``dict[str, dict[str, Any]]``.
dist/* regenerated per Phase 0 norm.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Drives the React UI's brand block, env switcher list, and approval-rationale dropdown. Read once at React-app boot and cached for the session lifetime via useUiHints(). - Extend UIConfig with brand_name, brand_logo_url, approval_rationale_templates, hitl_question_templates (all optional with empty defaults; existing badges/detail_fields/ tags fields and the frozen+forbid model_config are unchanged). - New api_ui_hints sidecar mirrors the api_session_full pattern (module-level import + add_routes(api_v1)). - Surface AppConfig on app.state.cfg in the lifespan so the endpoint can read ui + environments without re-loading YAML. - Bundle api_ui_hints.py after api_session_full.py. - Regen dist/* per the Phase 0 per-commit dist-regen norm.
Phase 0 / Task 5. Approach C extensibility: apps register bespoke UI
overlay views via cfg.ui.app_views; the framework UI's Selected-detail
panel discovers them and renders "App-specific views ->" links.
v2.0 ships one app per deployment, so the path's {app} segment is
informational only — multi-app filtering is v2.1 scope.
Sidecar module mirrors the api_session_full / api_ui_hints pattern.
Adds runtime/api_static.py side-car: mounts /assets/* and /fonts/*
from $ASR_WEB_DIST (default web/dist) and a catch-all
GET /{full_path:path} that returns index.html so the React Router
can pick up arbitrary URLs. /api/, /health, /docs, /openapi.json
are reserved and return a structured JSON 404 envelope so unknown
API paths are not shadowed by the SPA. When the bundle isn't built
yet, GET / returns a 503 with a "cd web && npm ci && rtk npm run build"
hint to help dev users.
build_app calls api_static.mount(fastapi_app) AFTER include_router
+ legacy redirects so the catch-all is the last route registered.
Bundler picks up api_static.py after api_recent_events.py.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…le flattens cleanly Phase 0 introduced five new side-car routing modules (api_session_full, api_ui_hints, api_apps_overlay, api_recent_events, api_static) that each exposed an ``add_routes(api_v1)`` (or ``mount(app)``) entry point and were called from ``runtime.api.build_app`` via the module-attribute form ``api_<name>.add_routes(api_v1)``. That works in source where the modules are imported, but breaks in the single-file bundle: 1. The bundler strips ``from runtime import (api_*)`` lines without creating module-namespace shims, so ``api_session_full`` is not a defined name at runtime → ``NameError``. 2. The four ``def add_routes`` defs collide at module scope when flattened — only the last one wins, so even with shims the wrong handler would be called. Fix: rename to unique, module-qualified function names and import them directly: api_session_full.add_routes → add_session_full_routes api_ui_hints.add_routes → add_ui_hints_routes api_apps_overlay.add_routes → add_apps_overlay_routes api_recent_events.add_routes → add_recent_events_routes api_static.mount → mount_static_assets In source, ``api.py`` now does ``from runtime.api_session_full import add_session_full_routes`` and calls the bare function name. In the bundle, the strip pass removes the import line and the bare call resolves to the unique flattened def. No collisions, no module-shim gymnastics. Verified end-to-end via TestClient against ``dist/app.py``: - GET /health → 200 - GET /api/v1/config/ui-hints → 200 with full UIConfig payload - GET /api/v1/apps/incident_management/ui-views → 200 [] - GET /incidents → 308 → /api/v1/sessions - POST /investigate → 308 → /api/v1/investigate All 1334 tests pass; coverage 88.75%; ruff/pyright/concept-leak/ skill-lint all green; ``dist/*`` regenerated. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
First task of Phase 1 (React UI v2.0). Scaffolds web/ at the repo root with pinned production-grade dependencies, strict TypeScript, and a minimal placeholder App that proves the build + dev-server work end to end. Why this shape: - Vite 7 + React 19 + TS 5.9 in strict mode (noUncheckedIndexedAccess, exactOptionalPropertyTypes) — the bar for the v2 UI is "no `any`, no fudged types". - Dev-server proxy /api -> http://localhost:8000 so we can hit the Phase-0 FastAPI backend from React without CORS in dev. - Top-level .gitignore updated so web/node_modules, web/dist, web/playwright-report, web/test-results never reach the index. Verification: - npm install: 362 packages, 0 vulnerabilities (npm 11.12.1, node 24.15.0). - npm run build: tsc -b clean, vite build emits dist/index.html (331 B) + dist/assets/index-*.js (193 kB / 60.8 kB gzip) + sourcemap. - npm run dev: serves the placeholder at http://localhost:5173/ with the expected #root mount-point and "ASR Operator Console" title. Note: @types/node added to support process.env.npm_package_version in vite.config.ts; types pulled in via tsconfig.node.json `types: ["node"]` (scoped to the build-config layer, not the app code). Subsequent tasks (12-20) layer in design tokens, Tailwind v4 config, shadcn-style components, query client, generated API types, etc.
`tsc -b --noEmit` errors with TS6310 because the referenced composite project tsconfig.node.json must emit `.d.ts`/`.tsbuildinfo` to satisfy project-references. The app-level tsconfig.json already sets `noEmit: true`, so the build script (`tsc -b && vite build`) and the typecheck script (`tsc -b`) both correctly type-check src/ without producing JS for the app. Verified: `npm run typecheck` exits clean.
ad630dd to
d730533
Compare
|
Warning Review the following alerts detected in dependencies. According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.
|
|

Summary
Phase 0 (backend foundation) and the start of Phase 1 (frontend scaffold) for the React UI v2.0 milestone. Spec + plan are gitignored agent artifacts (preserved in
\$CLAUDE_JOB_DIR/preserved/).Spec: `docs/superpowers/specs/2026-05-15-react-ui-design.md`
Plan: `docs/superpowers/plans/2026-05-15-react-ui-v2.md`
What's in this PR
Phase 0 — Backend foundation (10 task commits, all reviewed)
Phase 1 — Frontend scaffold (3 commits — start)
Quality gates (Phase 0)
Quality gates (Phase 1 partial)
Remaining work
Phase 1 (Tasks 13-20): vendor fonts · TS tokens · Vitest · Icon · Button · Pill · Input · Modal
Phase 2 (Tasks 21-31): API client, SSE/WS hooks, sessionReducer, all data hooks, selectedRef
Phase 3 (Tasks 32-36): Topbar · FlowStrip · SessionsRail · Statusbar
Phase 4 (Tasks 37-42): SessionCanvas · Transcript · Turn · HITLBand
Phase 5 (Tasks 43-50): MonitorRail + 6 panels
Phase 6 (Tasks 51-56): Modals + 3 E2E flows
Phase 7 (Tasks 57-61): Responsive (tablet + mobile)
Phase 8 (Tasks 62-69): Build / deploy / CI / docs / tag
Phase 9 (Tasks 70-71): Streamlit sunset
Known latent issue (NOT introduced by this PR)
`dist/app.py` cannot complete `uvicorn --factory` startup end-to-end due to a pre-existing bundler bug in `Orchestrator.create`: the function-local `from runtime.config import VectorConfig as _VectorConfig` (orchestrator.py:514) is stripped by the bundler but the call site references `_VectorConfig` → NameError. This same bug exists on `main` (`dist/app.py:14021`). The 1334-test suite uses `build_app(cfg)` directly through Python imports (no bundle involved) and is unaffected. Track for later cleanup.
Test plan
🤖 Generated with Claude Code